package com.example.zhujftest.hik.controller;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.example.zhujftest.hik.entity.User;
import com.example.zhujftest.hik.mapper.UserMapper;

/**
 * 
 * 原子性（Atomicity）：事务是一个原子操作，由一系列动作组成。事务的原子性确保动作要么全部完成，要么完全不起作用。
      一致性（Consistency）：一旦事务完成（不管成功还是失败），系统必须确保它所建模的业务处于一致的状态，而不会是部分完成部分失败。在现实中的数据不应该被破坏。
      隔离性（Isolation）：可能有许多事务会同时处理相同的数据，因此每个事务都应该与其他事务隔离开来，防止数据损坏。
      持久性（Durability）：一旦事务完成，无论发生什么系统错误，它的结果都不应该受到影响，这样就能从任何系统崩溃中恢复过来。通常情况下，事务的结果被写到持久化存储器中。
 * 
 * 
 * 
 */




/**
 * DEFAULT：这是默认值，表示使用底层数据库的默认隔离级别。对大部分数据库而言，通常这值就是：READ_COMMITTED。
 * READ_UNCOMMITTED：该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读，因此很少使用该隔离级别。
 * READ_COMMITTED：该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读，这也是大多数情况下的推荐值。
 * REPEATABLE_READ：该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询，并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询，这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
 * SERIALIZABLE：所有的事务依次逐个执行，这样事务之间就完全不可能产生干扰，也就是说，该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
 * 
 * 指定方法：通过使用isolation属性设置，例如：
 * @Transactional(isolation = Isolation.DEFAULT)
 * 
 * 注意：
 * 1.设置数据库的隔离级别一定要是在开启事务之前！
 * 2.
 * 后记：隔离级别的设置只对当前链接有效。对于使用MySQL命令窗口而言，一个窗口就相当于一个链接，
 * 当前窗口设置的隔离级别只对当前窗口中的事务有效；对于JDBC操作数据库来说，
 * 一个Connection对象相当于一个链接，
 * 而对于Connection对象设置的隔离级别只对该Connection对象有效，与其他链接Connection对象无关。
 * @author zhujianfeng7
 *
 */
@RestController
@RequestMapping("request")
public class TestController {

	@Autowired  
    private UserMapper userMapper;  
	
	/**
	 * 测试程序执行
	 * @throws Exception 
	 */
	
	@RequestMapping(value = "action")
	//在方法和类上面都可以  ,一般添加到方法上，因为只有增删改才需要事务
	@Transactional(value = "txManager1") 
	public void test() throws Exception{
		
		 User user = new User();  
	        user.setUserName("hahahahahah");  
	        user.setAge(55);  
	        userMapper.save(user);
	}
	
	/**
	 * 程序出现异常时，增删改是不能成功的
	 */
	@RequestMapping(value = "test")
	@Transactional(value = "txManager1")  
	public void test2(){
		
		 User user = new User();  
	        user.setUserName("haha");  
	        user.setAge(55);  
	        userMapper.save(user);  
	        System.out.println("插入用户信息"+user.getUserName());  
	        
	        if (user.getUserName().equals("haha")) {  
	            throw new IllegalArgumentException("出现异常QAQ");  
	        }  
	          
	        user = new User();  
	        user.setUserName("李四11111");  
	        user.setAge(23);  
	        userMapper.save(user);  
		
	}
	
	
	/**
     * rollbackFor 抛出异常之后回滚数据，即新纪录不会被写进数据库
     * 
     * @Transactional 使用的是org.springframework.transaction.annotation.Transactional; 
     *                   不是 javax.transaction.Transactional;
     *                   
     *                   
     * 注意： 如果异常被try｛｝catch｛｝了，事务就不回滚了，如果想让事务回滚必须再往外抛try｛｝catch｛throw Exception｝。
     * 如果既想使用try｛｝catch｛｝捕获事务，又想让事务回滚，可以使用TransactionAspectSupport.currentTransactionStatus()
                    .setRollbackOnly();
     */
	@RequestMapping(value = "test2")
	@Transactional(value = "txManager1",rollbackFor = { IllegalArgumentException.class })
    public void transaction_1() {
		User user = new User();  
        user.setUserName("haha");  
        user.setAge(55);  
        userMapper.save(user);  
        
        throw new IllegalArgumentException("\rollbackFor");
    }
	
	/**
     * noRollbackFor 抛出异常之后，不会滚数据，即新纪录还是会被写进数据库
     */
	@RequestMapping(value = "test3")
	@Transactional(value = "txManager1",noRollbackFor = { IllegalArgumentException.class })
	public void transaction_2() {
		User user = new User();  
		user.setUserName("haha");  
		user.setAge(55);  
		userMapper.save(user);  
		
		throw new IllegalArgumentException("\rollbackFor");
	}

	
	 /**
     * readOnly = true
     * 会出现异常：Queries leading to data modification are not allowed
     */
	@RequestMapping(value = "test4")
    @Transactional(value = "txManager1",readOnly = true)
    public void transaction_3() {
    	User user = new User();  
		user.setUserName("haha");  
		user.setAge(55);  
		userMapper.updateById(user);
		
    }

    
    //事务传播
    
    /**
	 * 1.REQUIRED 
	 * 必须要在事务下执行，如果当前存在事务则加入该事务，否则创建新的事务执行。例如：
	 * A方法调用B方法，A方法已经在事务下了，调用B的时候，B会加入到A的事务中，处于同一事务下，
	 * 如果出错所有的操作都将回滚。
	 * 
	 * 2.SUPPORTS
	 * 当前如果存在事务则加入该事务，如果没有就不用事务执行。
	 * 
	 * 3.MANDATORY
	 * 必须要在事务下执行，如果当前存在事务则加入该事务，如果不存在事务则抛异常。
	 * 
	 * 4.REQUIRES_NEW
	 * 必须要在一个新的事务下执行，如果当前存在事务，则会创建新的事务执行，如果出现错误不会回滚其它事务。
	 * 例如：A调用B，B出错不会导致A事务数据回滚。
	 * 
	 * 5.NOT_SUPPORTED
	 * 不能在事务下执行，如果当前存在事务则把当前事务挂起。
	 * 
	 * 6.NEVER
	 * 不能在事务下执行，如果当前存在事务则抛异常。
	 * 
	 * 
	 * 7.NESTED
	 * 如果当前存在事务，则会创建新的事务执行。具体的这个我也不是怎么清楚，很少用到。
	 * 
	 */
    
    @Transactional(value = "txManager1",propagation=Propagation.REQUIRED,noRollbackFor = { IllegalArgumentException.class })
    @RequestMapping(value = "test5")
    public void addUser() throws Exception {
    	User user = new User();  
		user.setUserName("haha");  
		user.setAge(55);  
		userMapper.save(user);
		testA();
    }
    
    
    @Transactional(value = "txManager1",propagation=Propagation.REQUIRED)
    @RequestMapping(value = "test6")
    public void addUser2() throws Exception {
    	User user = new User();  
    	user.setUserName("haha");  
    	user.setAge(55);  
    	userMapper.save(user);
    	testA();
    }
    
    @Transactional(value = "txManager1",propagation=Propagation.NOT_SUPPORTED)
    public void testA(){
    	throw new IllegalArgumentException("\rollbackFor");
    }
    
	
	
	/**
	 * 测试异步任务
	 */
    @RequestMapping(value = "aynctest")
	@Transactional(value = "txManager1",rollbackFor = { Exception.class })
    public void aynstransaction() {
		User user = new User();  
        user.setUserName("haha");  
        user.setAge(57);  
        userMapper.save(user);  
        
        //插入事务完成后才会执行，失败则不执行
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                System.out.println("send email after transaction commit...");
                taskExecutor.submit(new Notify());
            }
        }
       );
        //
        
        //throw new IllegalArgumentException("\rollbackFor");
    }
	

    class Notify implements  Runnable{
        
        @Override
        public void run() {
           System.out.println("执行异步任务");
        }
    }	
	//在方法中，如果调用了多个方法，想在某一个方法执行完后立马提交事务，而不是整个大方法执行完一起提交，则使用以下技术
	//https://www.cnblogs.com/ruiati/p/6027277.html
	

		
}
